package generator;

import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.ProgressCallback;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.mybatis.generator.internal.NullProgressCallback;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;


public class GeneratorConfig {

    private static void changeAttrValueByReflection(Object obj, String fieldName, Object newAttrValue) throws NoSuchFieldException, IllegalAccessException {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, newAttrValue);
    }

    public static void main(String[] args)throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        InputStream inputStream = GeneratorConfig.class.getResourceAsStream("/generatorConfig4Starp.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(inputStream);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);

        //开始想用包装器模式对MyBatisGenerator类实例进行包装，并对生成java和xml文件的类再进行包装，从而实现需要的功能，
        // 不过生成java和xml文件的类，都是在generate方法调用中生成的，而且中间又经过几个类间的调用，导致实现不太容易
        //所以最终采用反射来实现，简单粗暴
        //根据需要通过反射改写文件信息及内容并重新生成文件
        Field generatedXmlFilesField = MyBatisGenerator.class.getDeclaredField("generatedXmlFiles");
        generatedXmlFilesField.setAccessible(true);
        List<GeneratedXmlFile> generatedXmlFiles = (List<GeneratedXmlFile>) generatedXmlFilesField.get(myBatisGenerator);
        Field generatedJavaFilesField = MyBatisGenerator.class.getDeclaredField("generatedJavaFiles");
        generatedJavaFilesField.setAccessible(true);
        List<GeneratedJavaFile> generatedJavaFiles = (List<GeneratedJavaFile>) generatedJavaFilesField.get(myBatisGenerator);
        //修改文件信息和文件内容，主要包括接口类文件名，接口类内容中文件名（添加继承关系），xml引的接口类名

        Method writeGeneratedXmlFileMethod = MyBatisGenerator.class.getDeclaredMethod("writeGeneratedXmlFile", new Class[]{GeneratedXmlFile.class, ProgressCallback.class});
        writeGeneratedXmlFileMethod.setAccessible(true);

        Method writeGeneratedJavaFileMethod = MyBatisGenerator.class.getDeclaredMethod("writeGeneratedJavaFile", new Class[]{GeneratedJavaFile.class, ProgressCallback.class});
        writeGeneratedJavaFileMethod.setAccessible(true);

        StringConverter packageNameConverter = (str) -> {
            return str.replaceAll("base", "web");
        };

        StringConverter interfaceNameConverter = (str) -> {
            return str.replaceAll("(\\S+)Mapper", "$1Dao");
        };

        NullProgressCallback nullProgressCallback = new NullProgressCallback();
//
        for (GeneratedXmlFile gxf : generatedXmlFiles) {
            Field documentField = GeneratedXmlFile.class.getDeclaredField("document");
            documentField.setAccessible(true);
            Document document = (Document) documentField.get(gxf);
            List<Attribute> attributes = document.getRootElement().getAttributes();
            for (Attribute attribute : attributes) {
                if ("namespace".equals(attribute.getName())) {
                    //改xml的根节点的namesapce属性
                    changeAttrValueByReflection(attribute, "value", interfaceNameConverter.convert(attribute.getValue()));
                }
            }
            writeGeneratedXmlFileMethod.invoke(myBatisGenerator, new Object[]{gxf, nullProgressCallback});
        }

        for (GeneratedJavaFile gjf : generatedJavaFiles) {
            CompilationUnit compilationUnit = gjf.getCompilationUnit();
            FullyQualifiedJavaType type = compilationUnit.getType();
            String packageName = type.getPackageName();
            if(packageName.indexOf("model") <= 0 ) {
                //改接口类的类名，添加继承关系
                String shortName = type.getShortName();
                String fullyQualifiedName = type.getFullyQualifiedName();
                String superClassFullName = packageNameConverter.convert(packageName) + "." + shortName.replaceAll("Mapper", "4WebDao");
                FullyQualifiedJavaType superclassType = new FullyQualifiedJavaType(superClassFullName);
                Set<FullyQualifiedJavaType> importedTypes = compilationUnit.getImportedTypes();
                Set<FullyQualifiedJavaType> superInterfaceTypes = compilationUnit.getSuperInterfaceTypes();
                Field cField = importedTypes.getClass().getSuperclass().getDeclaredField("c");
                cField.setAccessible(true);
                Collection c = (Collection) cField.get(importedTypes);
                c.add(superclassType);
                superInterfaceTypes.add(superclassType);
                changeAttrValueByReflection(type, "baseShortName", interfaceNameConverter.convert(shortName));
                changeAttrValueByReflection(type, "baseQualifiedName", interfaceNameConverter.convert(fullyQualifiedName));
            }
            writeGeneratedJavaFileMethod.invoke(myBatisGenerator, gjf, nullProgressCallback);
        }
        System.out.println(">>>>>>>>>>>>>>>>>>\n完成");
    }
}

@FunctionalInterface
interface StringConverter{
    String convert(String src);
}